/*
 * Copyright 2017, Blender Foundation.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * Contributor(s): Antonio Vazquez
 *
 */

/** \file blender/draw/engines/gpencil/gpencil_shader_fx.c
 *  \ingroup draw
 */
#include "DNA_gpencil_types.h"
#include "DNA_shader_fx_types.h"
#include "DNA_screen_types.h"
#include "DNA_view3d_types.h"
#include "DNA_camera_types.h"

#include "BKE_gpencil.h"
#include "BKE_shader_fx.h"

#include "DRW_engine.h"
#include "DRW_render.h"

#include "BKE_camera.h"

#include "ED_view3d.h"
#include "ED_gpencil.h"

#include "gpencil_engine.h"

extern char datatoc_gpencil_fx_blur_frag_glsl[];
extern char datatoc_gpencil_fx_colorize_frag_glsl[];
extern char datatoc_gpencil_fx_flip_frag_glsl[];
extern char datatoc_gpencil_fx_light_frag_glsl[];
extern char datatoc_gpencil_fx_pixel_frag_glsl[];
extern char datatoc_gpencil_fx_rim_prepare_frag_glsl[];
extern char datatoc_gpencil_fx_rim_resolve_frag_glsl[];
extern char datatoc_gpencil_fx_shadow_prepare_frag_glsl[];
extern char datatoc_gpencil_fx_shadow_resolve_frag_glsl[];
extern char datatoc_gpencil_fx_glow_prepare_frag_glsl[];
extern char datatoc_gpencil_fx_glow_resolve_frag_glsl[];
extern char datatoc_gpencil_fx_swirl_frag_glsl[];
extern char datatoc_gpencil_fx_wave_frag_glsl[];

/* verify if this fx is active */
static bool effect_is_active(bGPdata *gpd, ShaderFxData *fx, bool is_render)
{
	if (fx == NULL) {
		return false;
	}

	if (gpd == NULL) {
		return false;
	}

	bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd);
	if (((fx->mode & eShaderFxMode_Editmode) == 0) && (is_edit)) {
		return false;
	}

	if (((fx->mode & eShaderFxMode_Realtime) && (is_render == false)) ||
	    ((fx->mode & eShaderFxMode_Render) && (is_render == true)))
	{
		return true;
	}

	return false;
}

/**
 * Get normal of draw using one stroke of visible layer
 * \param gpd: GP datablock
 * \param r_point: Point on plane
 * \param r_normal: Normal vector
 */
static bool get_normal_vector(bGPdata *gpd, float r_point[3], float r_normal[3])
{
	for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
		if (gpl->flag & GP_LAYER_HIDE)
			continue;

		/* get frame  */
		bGPDframe *gpf = gpl->actframe;
		if (gpf == NULL)
			continue;
		for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
			if (gps->totpoints >= 3) {
				bGPDspoint *pt = &gps->points[0];
				BKE_gpencil_stroke_normal(gps, r_normal);
				/* in some weird situations, the normal cannot be calculated, so try next stroke */
				if ((r_normal[0] != 0.0f) || (r_normal[1] != 0.0f) || (r_normal[2] != 0.0f)) {
					copy_v3_v3(r_point, &pt->x);
					return true;
				}
			}
		}
	}

	return false;
}

/* helper to get near and far depth of field values */
static void GPENCIL_dof_nearfar(Object *camera, float coc, float nearfar[2])
{
	if (camera == NULL) {
		return;
	}

	const DRWContextState *draw_ctx = DRW_context_state_get();
	Scene *scene = draw_ctx->scene;
	Camera *cam = (Camera *)camera->data;

	float fstop = cam->gpu_dof.fstop;
	float focus_dist = BKE_camera_object_dof_distance(camera);
	float focal_len = cam->lens;

	/* this is factor that converts to the scene scale. focal length and sensor are expressed in mm
	 * unit.scale_length is how many meters per blender unit we have. We want to convert to blender units though
	 * because the shader reads coordinates in world space, which is in blender units.
	 * Note however that focus_distance is already in blender units and shall not be scaled here (see T48157). */
	float scale = (scene->unit.system) ? scene->unit.scale_length : 1.0f;
	float scale_camera = 0.001f / scale;
	/* we want radius here for the aperture number  */
	float aperture_scaled = 0.5f * scale_camera * focal_len / fstop;
	float focal_len_scaled = scale_camera * focal_len;

	float hyperfocal = (focal_len_scaled * focal_len_scaled) / (aperture_scaled * coc);
	nearfar[0] = (hyperfocal * focus_dist) / (hyperfocal + focal_len);
	nearfar[1] = (hyperfocal * focus_dist) / (hyperfocal - focal_len);
}

/* ****************  Shader Effects ***************************** */

/* Gaussian Blur FX
 * The effect is done using two shading groups because is faster to apply horizontal
 * and vertical in different operations.
 */
static void DRW_gpencil_fx_blur(
        ShaderFxData *fx, int ob_idx, GPENCIL_e_data *e_data,
        GPENCIL_Data *vedata, tGPencilObjectCache *cache)
{
	if (fx == NULL) {
		return;
	}

	BlurShaderFxData *fxd = (BlurShaderFxData *)fx;

	GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
	GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
	const DRWContextState *draw_ctx = DRW_context_state_get();
	View3D *v3d = draw_ctx->v3d;
	RegionView3D *rv3d = draw_ctx->rv3d;
	DRWShadingGroup *fx_shgrp;

	fxd->blur[0] = fxd->radius[0];
	fxd->blur[1] = fxd->radius[1];

	/* init weight */
	if (fxd->flag & FX_BLUR_DOF_MODE) {
		/* viewport and opengl render */
		Object *camera = NULL;
		if (rv3d) {
			if (rv3d->persp == RV3D_CAMOB) {
				camera = v3d->camera;
			}
		}
		else {
			camera = stl->storage->camera;
		}

		if (camera) {
			float nearfar[2];
			GPENCIL_dof_nearfar(camera, fxd->coc, nearfar);
			float zdepth = stl->g_data->gp_object_cache[ob_idx].zdepth;
			/* the object is on focus area */
			if ((zdepth >= nearfar[0]) && (zdepth <= nearfar[1])) {
				fxd->blur[0] = 0;
				fxd->blur[1] = 0;
			}
			else {
				float f;
				if (zdepth < nearfar[0]) {
					f = nearfar[0] - zdepth;
				}
				else {
					f = zdepth - nearfar[1];
				}
				fxd->blur[0] = f;
				fxd->blur[1] = f;
				CLAMP2(&fxd->blur[0], 0, fxd->radius[0]);
			}
		}
		else {
			/* if not camera view, the blur is disabled */
			fxd->blur[0] = 0;
			fxd->blur[1] = 0;
		}
	}

	GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();

	fx_shgrp = DRW_shgroup_create(
	        e_data->gpencil_fx_blur_sh,
	        psl->fx_shader_pass_blend);
	DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
	DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1);
	DRW_shgroup_uniform_int(fx_shgrp, "blur", &fxd->blur[0], 2);

	DRW_shgroup_uniform_vec3(fx_shgrp, "loc", &cache->loc[0], 1);
	DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1);
	DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &cache->pixfactor, 1);

	fxd->runtime.fx_sh = fx_shgrp;
}

/* Colorize FX */
static void DRW_gpencil_fx_colorize(
        ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata)
{
	if (fx == NULL) {
		return;
	}
	ColorizeShaderFxData *fxd = (ColorizeShaderFxData *)fx;
	GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
	DRWShadingGroup *fx_shgrp;

	GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();
	fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_colorize_sh, psl->fx_shader_pass);
	DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
	DRW_shgroup_uniform_vec4(fx_shgrp, "low_color", &fxd->low_color[0], 1);
	DRW_shgroup_uniform_vec4(fx_shgrp, "high_color", &fxd->high_color[0], 1);
	DRW_shgroup_uniform_int(fx_shgrp, "mode", &fxd->mode, 1);
	DRW_shgroup_uniform_float(fx_shgrp, "factor", &fxd->factor, 1);

	fxd->runtime.fx_sh = fx_shgrp;
}

/* Flip FX */
static void DRW_gpencil_fx_flip(
        ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata)
{
	if (fx == NULL) {
		return;
	}
	FlipShaderFxData *fxd = (FlipShaderFxData *)fx;
	GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
	DRWShadingGroup *fx_shgrp;

	fxd->flipmode = 100;
	/* the number works as bit flag */
	if (fxd->flag & FX_FLIP_HORIZONTAL) {
		fxd->flipmode += 10;
	}
	if (fxd->flag & FX_FLIP_VERTICAL) {
		fxd->flipmode += 1;
	}

	GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();
	fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_flip_sh, psl->fx_shader_pass);
	DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
	DRW_shgroup_uniform_int(fx_shgrp, "flipmode", &fxd->flipmode, 1);

	DRW_shgroup_uniform_vec2(fx_shgrp, "wsize", DRW_viewport_size_get(), 1);

	fxd->runtime.fx_sh = fx_shgrp;
}

/* Light FX */
static void DRW_gpencil_fx_light(
        ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata,
        tGPencilObjectCache *cache)
{
	if (fx == NULL) {
		return;
	}
	LightShaderFxData *fxd = (LightShaderFxData *)fx;

	if (fxd->object == NULL) {
		return;
	}
	GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
	GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
	DRWShadingGroup *fx_shgrp;

	GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();
	fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_light_sh, psl->fx_shader_pass);
	DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);

	DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1);

	/* location of the light using obj location as origin */
	copy_v3_v3(fxd->loc, fxd->object->obmat[3]);

	/* Calc distance to strokes plane
	 * The w component of location is used to transfer the distance to drawing plane
	 */
	float r_point[3], r_normal[3];
	float r_plane[4];
	bGPdata *gpd = cache->gpd;
	if (!get_normal_vector(gpd, r_point, r_normal)) {
		return;
	}
	mul_mat3_m4_v3(cache->obmat, r_normal); /* only rotation component */
	plane_from_point_normal_v3(r_plane, r_point, r_normal);
	float dt = dist_to_plane_v3(fxd->object->obmat[3], r_plane);
	fxd->loc[3] = dt; /* use last element to save it */

	DRW_shgroup_uniform_vec4(fx_shgrp, "loc", &fxd->loc[0], 1);

	DRW_shgroup_uniform_float(fx_shgrp, "energy", &fxd->energy, 1);
	DRW_shgroup_uniform_float(fx_shgrp, "ambient", &fxd->ambient, 1);

	DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1);
	DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &cache->pixfactor, 1);

	fxd->runtime.fx_sh = fx_shgrp;
}

/* Pixelate FX */
static void DRW_gpencil_fx_pixel(
        ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata,
        tGPencilObjectCache *cache)
{
	if (fx == NULL) {
		return;
	}
	PixelShaderFxData *fxd = (PixelShaderFxData *)fx;

	GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
	GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
	DRWShadingGroup *fx_shgrp;
	bGPdata *gpd = cache->gpd;

	fxd->size[2] = (int)fxd->flag & FX_PIXEL_USE_LINES;

	GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();
	fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_pixel_sh, psl->fx_shader_pass);
	DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
	DRW_shgroup_uniform_int(fx_shgrp, "size", &fxd->size[0], 3);
	DRW_shgroup_uniform_vec4(fx_shgrp, "color", &fxd->rgba[0], 1);

	DRW_shgroup_uniform_vec3(fx_shgrp, "loc", &cache->loc[0], 1);
	DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1);
	DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1);

	fxd->runtime.fx_sh = fx_shgrp;
}

/* Rim FX */
static void DRW_gpencil_fx_rim(
        ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata,
        tGPencilObjectCache *cache)
{
	if (fx == NULL) {
		return;
	}
	RimShaderFxData *fxd = (RimShaderFxData *)fx;

	GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
	GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
	DRWShadingGroup *fx_shgrp;

	GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();
	/* prepare pass */
	fx_shgrp = DRW_shgroup_create(
	        e_data->gpencil_fx_rim_prepare_sh,
	        psl->fx_shader_pass_blend);
	DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
	DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1);

	DRW_shgroup_uniform_int(fx_shgrp, "offset", &fxd->offset[0], 2);
	DRW_shgroup_uniform_vec3(fx_shgrp, "rim_color", &fxd->rim_rgb[0], 1);
	DRW_shgroup_uniform_vec3(fx_shgrp, "mask_color", &fxd->mask_rgb[0], 1);

	DRW_shgroup_uniform_vec3(fx_shgrp, "loc", &cache->loc[0], 1);
	DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1);
	DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &cache->pixfactor, 1);

	fxd->runtime.fx_sh = fx_shgrp;

	/* blur pass */
	fx_shgrp = DRW_shgroup_create(
	        e_data->gpencil_fx_blur_sh,
	        psl->fx_shader_pass_blend);
	DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_fx);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_fx);
	DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1);
	DRW_shgroup_uniform_int(fx_shgrp, "blur", &fxd->blur[0], 2);

	DRW_shgroup_uniform_vec3(fx_shgrp, "loc", &cache->loc[0], 1);
	DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1);
	DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &cache->pixfactor, 1);

	fxd->runtime.fx_sh_b = fx_shgrp;

	/* resolve pass */
	fx_shgrp = DRW_shgroup_create(
	        e_data->gpencil_fx_rim_resolve_sh,
	        psl->fx_shader_pass_blend);
	DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeRim", &e_data->temp_color_tx_fx);
	DRW_shgroup_uniform_vec3(fx_shgrp, "mask_color", &fxd->mask_rgb[0], 1);
	DRW_shgroup_uniform_int(fx_shgrp, "mode", &fxd->mode, 1);

	fxd->runtime.fx_sh_c = fx_shgrp;
}

/* Shadow FX */
static void DRW_gpencil_fx_shadow(
        ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata,
        tGPencilObjectCache *cache)
{
	if (fx == NULL) {
		return;
	}
	ShadowShaderFxData *fxd = (ShadowShaderFxData *)fx;
	if ((!fxd->object) && (fxd->flag & FX_SHADOW_USE_OBJECT)) {
		fxd->runtime.fx_sh = NULL;
		fxd->runtime.fx_sh_b = NULL;
		fxd->runtime.fx_sh_c = NULL;
		return;
	}

	GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
	GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
	DRWShadingGroup *fx_shgrp;

	GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();
	/* prepare pass */
	fx_shgrp = DRW_shgroup_create(
		e_data->gpencil_fx_shadow_prepare_sh,
		psl->fx_shader_pass_blend);
	DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
	DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1);

	DRW_shgroup_uniform_int(fx_shgrp, "offset", &fxd->offset[0], 2);
	DRW_shgroup_uniform_float(fx_shgrp, "scale", &fxd->scale[0], 2);
	DRW_shgroup_uniform_float(fx_shgrp, "rotation", &fxd->rotation, 1);
	DRW_shgroup_uniform_vec4(fx_shgrp, "shadow_color", &fxd->shadow_rgba[0], 1);

	if ((fxd->object) && (fxd->flag & FX_SHADOW_USE_OBJECT)) {
		DRW_shgroup_uniform_vec3(fx_shgrp, "loc", fxd->object->obmat[3], 1);
	}
	else {
		DRW_shgroup_uniform_vec3(fx_shgrp, "loc", &cache->loc[0], 1);
	}

	const int nowave = -1;
	if (fxd->flag & FX_SHADOW_USE_WAVE) {
		DRW_shgroup_uniform_int(fx_shgrp, "orientation", &fxd->orientation, 1);
	}
	else {
		DRW_shgroup_uniform_int(fx_shgrp, "orientation", &nowave, 1);
	}
	DRW_shgroup_uniform_float(fx_shgrp, "amplitude", &fxd->amplitude, 1);
	DRW_shgroup_uniform_float(fx_shgrp, "period", &fxd->period, 1);
	DRW_shgroup_uniform_float(fx_shgrp, "phase", &fxd->phase, 1);

	DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1);
	DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &cache->pixfactor, 1);

	fxd->runtime.fx_sh = fx_shgrp;

	/* blur pass */
	fx_shgrp = DRW_shgroup_create(
		e_data->gpencil_fx_blur_sh,
		psl->fx_shader_pass_blend);
	DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_fx);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_fx);
	DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1);
	DRW_shgroup_uniform_int(fx_shgrp, "blur", &fxd->blur[0], 2);

	DRW_shgroup_uniform_vec3(fx_shgrp, "loc", &cache->loc[0], 1);
	DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1);
	DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &cache->pixfactor, 1);

	fxd->runtime.fx_sh_b = fx_shgrp;

	/* resolve pass */
	fx_shgrp = DRW_shgroup_create(
		e_data->gpencil_fx_shadow_resolve_sh,
		psl->fx_shader_pass_blend);
	DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "shadowColor", &e_data->temp_color_tx_fx);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "shadowDepth", &e_data->temp_depth_tx_fx);

	fxd->runtime.fx_sh_c = fx_shgrp;
}

/* Glow FX */
static void DRW_gpencil_fx_glow(
        ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata,
        tGPencilObjectCache *cache)
{
	if (fx == NULL) {
		return;
	}
	GlowShaderFxData *fxd = (GlowShaderFxData *)fx;

	GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
	GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
	DRWShadingGroup *fx_shgrp;

	GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();
	/* prepare pass */
	fx_shgrp = DRW_shgroup_create(
		e_data->gpencil_fx_glow_prepare_sh,
		psl->fx_shader_pass_blend);
	DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);

	DRW_shgroup_uniform_vec3(fx_shgrp, "glow_color", &fxd->glow_color[0], 1);
	DRW_shgroup_uniform_vec3(fx_shgrp, "select_color", &fxd->select_color[0], 1);
	DRW_shgroup_uniform_int(fx_shgrp, "mode", &fxd->mode, 1);
	DRW_shgroup_uniform_float(fx_shgrp, "threshold", &fxd->threshold, 1);

	fxd->runtime.fx_sh = fx_shgrp;

	/* blur pass */
	fx_shgrp = DRW_shgroup_create(
		e_data->gpencil_fx_blur_sh,
		psl->fx_shader_pass_blend);
	DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_fx);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_fx);
	DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1);
	DRW_shgroup_uniform_int(fx_shgrp, "blur", &fxd->blur[0], 2);

	DRW_shgroup_uniform_vec3(fx_shgrp, "loc", &cache->loc[0], 1);
	DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1);
	DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &cache->pixfactor, 1);

	fxd->runtime.fx_sh_b = fx_shgrp;

	/* resolve pass */
	fx_shgrp = DRW_shgroup_create(
		e_data->gpencil_fx_glow_resolve_sh,
		psl->fx_shader_pass_blend);
	DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "glowColor", &e_data->temp_color_tx_fx);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "glowDepth", &e_data->temp_depth_tx_fx);

	/* reuse field */
	DRW_shgroup_uniform_int(fx_shgrp, "alpha_mode", &fxd->blur[1], 1);

	fxd->runtime.fx_sh_c = fx_shgrp;
}

/* Swirl FX */
static void DRW_gpencil_fx_swirl(
        ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata,
        tGPencilObjectCache *cache)
{
	if (fx == NULL) {
		return;
	}
	SwirlShaderFxData *fxd = (SwirlShaderFxData *)fx;
	if (fxd->object == NULL) {
		return;
	}

	GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
	GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
	DRWShadingGroup *fx_shgrp;

	fxd->transparent = (int)fxd->flag & FX_SWIRL_MAKE_TRANSPARENT;

	GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();
	fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_swirl_sh, psl->fx_shader_pass);
	DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);

	DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1);

	DRW_shgroup_uniform_vec3(fx_shgrp, "loc", fxd->object->obmat[3], 1);

	DRW_shgroup_uniform_int(fx_shgrp, "radius", &fxd->radius, 1);
	DRW_shgroup_uniform_float(fx_shgrp, "angle", &fxd->angle, 1);
	DRW_shgroup_uniform_int(fx_shgrp, "transparent", &fxd->transparent, 1);

	DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1);
	DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &cache->pixfactor, 1);

	fxd->runtime.fx_sh = fx_shgrp;
}

/* Wave Distorsion FX */
static void DRW_gpencil_fx_wave(
        ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata)
{
	if (fx == NULL) {
		return;
	}

	WaveShaderFxData *fxd = (WaveShaderFxData *)fx;

	GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
	GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();

	DRWShadingGroup *fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_wave_sh, psl->fx_shader_pass);
	DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
	DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
	DRW_shgroup_uniform_float(fx_shgrp, "amplitude", &fxd->amplitude, 1);
	DRW_shgroup_uniform_float(fx_shgrp, "period", &fxd->period, 1);
	DRW_shgroup_uniform_float(fx_shgrp, "phase", &fxd->phase, 1);
	DRW_shgroup_uniform_int(fx_shgrp, "orientation", &fxd->orientation, 1);
	DRW_shgroup_uniform_vec2(fx_shgrp, "wsize", DRW_viewport_size_get(), 1);

	fxd->runtime.fx_sh = fx_shgrp;
}

/* ************************************************************** */

/* create all FX shaders */
void GPENCIL_create_fx_shaders(GPENCIL_e_data *e_data)
{
	/* fx shaders (all in screen space) */
	if (!e_data->gpencil_fx_blur_sh) {
		e_data->gpencil_fx_blur_sh = DRW_shader_create_fullscreen(
			datatoc_gpencil_fx_blur_frag_glsl, NULL);
	}
	if (!e_data->gpencil_fx_colorize_sh) {
		e_data->gpencil_fx_colorize_sh = DRW_shader_create_fullscreen(
			datatoc_gpencil_fx_colorize_frag_glsl, NULL);
	}
	if (!e_data->gpencil_fx_flip_sh) {
		e_data->gpencil_fx_flip_sh = DRW_shader_create_fullscreen(
			datatoc_gpencil_fx_flip_frag_glsl, NULL);
	}
	if (!e_data->gpencil_fx_light_sh) {
		e_data->gpencil_fx_light_sh = DRW_shader_create_fullscreen(
			datatoc_gpencil_fx_light_frag_glsl, NULL);
	}
	if (!e_data->gpencil_fx_pixel_sh) {
		e_data->gpencil_fx_pixel_sh = DRW_shader_create_fullscreen(
			datatoc_gpencil_fx_pixel_frag_glsl, NULL);
	}
	if (!e_data->gpencil_fx_rim_prepare_sh) {
		e_data->gpencil_fx_rim_prepare_sh = DRW_shader_create_fullscreen(
			datatoc_gpencil_fx_rim_prepare_frag_glsl, NULL);

		e_data->gpencil_fx_rim_resolve_sh = DRW_shader_create_fullscreen(
			datatoc_gpencil_fx_rim_resolve_frag_glsl, NULL);
	}
	if (!e_data->gpencil_fx_shadow_prepare_sh) {
		e_data->gpencil_fx_shadow_prepare_sh = DRW_shader_create_fullscreen(
			datatoc_gpencil_fx_shadow_prepare_frag_glsl, NULL);

		e_data->gpencil_fx_shadow_resolve_sh = DRW_shader_create_fullscreen(
			datatoc_gpencil_fx_shadow_resolve_frag_glsl, NULL);
	}
	if (!e_data->gpencil_fx_glow_prepare_sh) {
		e_data->gpencil_fx_glow_prepare_sh = DRW_shader_create_fullscreen(
			datatoc_gpencil_fx_glow_prepare_frag_glsl, NULL);

		e_data->gpencil_fx_glow_resolve_sh = DRW_shader_create_fullscreen(
			datatoc_gpencil_fx_glow_resolve_frag_glsl, NULL);
	}
	if (!e_data->gpencil_fx_swirl_sh) {
		e_data->gpencil_fx_swirl_sh = DRW_shader_create_fullscreen(
			datatoc_gpencil_fx_swirl_frag_glsl, NULL);
	}
	if (!e_data->gpencil_fx_wave_sh) {
		e_data->gpencil_fx_wave_sh = DRW_shader_create_fullscreen(
			datatoc_gpencil_fx_wave_frag_glsl, NULL);
	}
}

/* free FX shaders */
void GPENCIL_delete_fx_shaders(GPENCIL_e_data *e_data)
{
	DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_blur_sh);
	DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_colorize_sh);
	DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_flip_sh);
	DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_light_sh);
	DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_pixel_sh);
	DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_rim_prepare_sh);
	DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_rim_resolve_sh);
	DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_shadow_prepare_sh);
	DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_shadow_resolve_sh);
	DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_glow_prepare_sh);
	DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_glow_resolve_sh);
	DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_swirl_sh);
	DRW_SHADER_FREE_SAFE(e_data->gpencil_fx_wave_sh);
}

/* create all passes used by FX */
void GPENCIL_create_fx_passes(GPENCIL_PassList *psl)
{
	psl->fx_shader_pass = DRW_pass_create(
	        "GPencil Shader FX Pass",
	        DRW_STATE_WRITE_COLOR |
	        DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS);
	psl->fx_shader_pass_blend = DRW_pass_create(
	        "GPencil Shader FX Pass",
	        DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND |
	        DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS);
}


/* prepare fx shading groups */
void DRW_gpencil_fx_prepare(
        GPENCIL_e_data *e_data, GPENCIL_Data *vedata,
        tGPencilObjectCache *cache)
{
	GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
	int ob_idx = cache->idx;

	if (cache->shader_fx.first == NULL) {
		return;
	}
	/* loop FX */
	for (ShaderFxData *fx = cache->shader_fx.first; fx; fx = fx->next) {
		if (effect_is_active(cache->gpd, fx, stl->storage->is_render)) {
			switch (fx->type) {
				case eShaderFxType_Blur:
					DRW_gpencil_fx_blur(fx, ob_idx, e_data, vedata, cache);
					break;
				case eShaderFxType_Colorize:
					DRW_gpencil_fx_colorize(fx, e_data, vedata);
					break;
				case eShaderFxType_Flip:
					DRW_gpencil_fx_flip(fx, e_data, vedata);
					break;
				case eShaderFxType_Light:
					DRW_gpencil_fx_light(fx, e_data, vedata, cache);
					break;
				case eShaderFxType_Pixel:
					DRW_gpencil_fx_pixel(fx, e_data, vedata, cache);
					break;
				case eShaderFxType_Rim:
					DRW_gpencil_fx_rim(fx, e_data, vedata, cache);
					break;
				case eShaderFxType_Shadow:
					DRW_gpencil_fx_shadow(fx, e_data, vedata, cache);
					break;
				case eShaderFxType_Glow:
					DRW_gpencil_fx_glow(fx, e_data, vedata, cache);
					break;
				case eShaderFxType_Swirl:
					DRW_gpencil_fx_swirl(fx, e_data, vedata, cache);
					break;
				case eShaderFxType_Wave:
					DRW_gpencil_fx_wave(fx, e_data, vedata);
					break;
				default:
					break;
			}
		}
	}

}

/* helper to draw one FX pass and do ping-pong copy */
static void gpencil_draw_fx_pass(
        GPENCIL_e_data *e_data,
        GPENCIL_PassList *psl,
        GPENCIL_FramebufferList *fbl,
        DRWShadingGroup *shgrp, bool blend)
{
	if (shgrp == NULL) {
		return;
	}

	const float clearcol[4] = {0.0f};
	GPU_framebuffer_bind(fbl->temp_fb_b);
	GPU_framebuffer_clear_color_depth(fbl->temp_fb_b, clearcol, 1.0f);

	/* draw effect pass in temp texture (B) using as source the previous image
	 * existing in the other temp texture (A) */
	if (!blend) {
		DRW_draw_pass_subset(psl->fx_shader_pass, shgrp, shgrp);
	}
	else {
		DRW_draw_pass_subset(psl->fx_shader_pass_blend, shgrp, shgrp);
	}

	/* copy pass from b to a for ping-pong frame buffers */
	e_data->input_depth_tx = e_data->temp_depth_tx_b;
	e_data->input_color_tx = e_data->temp_color_tx_b;

	GPU_framebuffer_bind(fbl->temp_fb_a);
	GPU_framebuffer_clear_color_depth(fbl->temp_fb_a, clearcol, 1.0f);
	DRW_draw_pass(psl->mix_pass_noblend);
}

/* helper to manage gaussian blur passes */
static void draw_gpencil_blur_passes(
        GPENCIL_e_data *e_data,
        GPENCIL_Data *vedata,
        BlurShaderFxData *fxd)
{
	if (fxd->runtime.fx_sh == NULL) {
		return;
	}

	GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
	GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl;
	DRWShadingGroup *shgrp = fxd->runtime.fx_sh;
	int samples = fxd->samples;

	float bx = fxd->blur[0];
	float by = fxd->blur[1];

	/* the blur is done in two steps (Hor/Ver) because is faster and
	 * gets better result
	 *
	 * samples could be 0 and disable de blur effects because sometimes
	 * is easier animate the number of samples only, instead to animate the
	 * hide/unhide and the number of samples to make some effects.
	 */
	for (int b = 0; b < samples; b++) {
		/* horizontal */
		if (bx > 0) {
			fxd->blur[0] = bx;
			fxd->blur[1] = 0;
			gpencil_draw_fx_pass(e_data, psl, fbl, shgrp, true);
		}
		/* vertical */
		if (by > 0) {
			fxd->blur[0] = 0;
			fxd->blur[1] = by;
			gpencil_draw_fx_pass(e_data, psl, fbl, shgrp, true);
		}
	}
}

/* blur intermediate pass */
static void draw_gpencil_midpass_blur(
        GPENCIL_Data *vedata,
        ShaderFxData_Runtime *runtime)
{
	GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
	GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl;
	const float clearcol[4] = {0.0f};

	GPU_framebuffer_bind(fbl->temp_fb_b);
	GPU_framebuffer_clear_color_depth(fbl->temp_fb_b, clearcol, 1.0f);
	DRW_draw_pass_subset(psl->fx_shader_pass_blend,
		runtime->fx_sh_b, runtime->fx_sh_b);

	/* copy pass from b for ping-pong frame buffers */
	GPU_framebuffer_bind(fbl->temp_fb_fx);
	GPU_framebuffer_clear_color_depth(fbl->temp_fb_fx, clearcol, 1.0f);
	DRW_draw_pass(psl->mix_pass_noblend);
}

/* do blur of mid passes */
static void draw_gpencil_do_blur(
        GPENCIL_e_data *e_data,
        GPENCIL_Data *vedata,
        ShaderFxData_Runtime *runtime,
        int samples, int bx, int by, int blur[2])
{
	e_data->input_depth_tx = e_data->temp_depth_tx_b;
	e_data->input_color_tx = e_data->temp_color_tx_b;

	if ((samples > 0) && ((bx > 0) || (by > 0))) {
		for (int x = 0; x < samples; x++) {

			/* horizontal */
			blur[0] = bx;
			blur[1] = 0;
			draw_gpencil_midpass_blur(vedata, runtime);

			/* Vertical */
			blur[0] = 0;
			blur[1] = by;
			draw_gpencil_midpass_blur(vedata, runtime);

			blur[0] = bx;
			blur[1] = by;
		}
	}
}

/* helper to draw RIM passes */
static void draw_gpencil_rim_passes(
        GPENCIL_e_data *e_data,
        GPENCIL_Data *vedata,
        RimShaderFxData *fxd)
{
	if (fxd->runtime.fx_sh_b == NULL) {
		return;
	}

	GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
	GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl;

	const float clearcol[4] = {0.0f};

	/* prepare mask */
	GPU_framebuffer_bind(fbl->temp_fb_fx);
	GPU_framebuffer_clear_color_depth(fbl->temp_fb_fx, clearcol, 1.0f);
	DRW_draw_pass_subset(
	        psl->fx_shader_pass_blend,
	        fxd->runtime.fx_sh, fxd->runtime.fx_sh);

	/* blur rim */
	draw_gpencil_do_blur(
	        e_data, vedata, &fxd->runtime,
	        fxd->samples,
	        fxd->blur[0], fxd->blur[1],
	        &fxd->blur[0]);

	/* resolve */
	GPU_framebuffer_bind(fbl->temp_fb_b);
	GPU_framebuffer_clear_color_depth(fbl->temp_fb_b, clearcol, 1.0f);
	DRW_draw_pass_subset(
	        psl->fx_shader_pass_blend,
	        fxd->runtime.fx_sh_c, fxd->runtime.fx_sh_c);

	/* copy pass from b to a for ping-pong frame buffers */
	e_data->input_depth_tx = e_data->temp_depth_tx_b;
	e_data->input_color_tx = e_data->temp_color_tx_b;

	GPU_framebuffer_bind(fbl->temp_fb_a);
	GPU_framebuffer_clear_color_depth(fbl->temp_fb_a, clearcol, 1.0f);
	DRW_draw_pass(psl->mix_pass_noblend);
}

/* helper to draw SHADOW passes */
static void draw_gpencil_shadow_passes(
        GPENCIL_e_data *e_data,
        GPENCIL_Data *vedata,
        ShadowShaderFxData *fxd)
{
	if (fxd->runtime.fx_sh_b == NULL) {
		return;
	}

	GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
	GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl;
	const float clearcol[4] = {0.0f};

	/* prepare shadow */
	GPU_framebuffer_bind(fbl->temp_fb_fx);
	GPU_framebuffer_clear_color_depth(fbl->temp_fb_fx, clearcol, 1.0f);
	DRW_draw_pass_subset(
		psl->fx_shader_pass_blend,
		fxd->runtime.fx_sh, fxd->runtime.fx_sh);

	/* blur shadow */
	draw_gpencil_do_blur(
	        e_data, vedata, &fxd->runtime,
	        fxd->samples,
	        fxd->blur[0], fxd->blur[1],
	        &fxd->blur[0]);

	/* resolve */
	GPU_framebuffer_bind(fbl->temp_fb_b);
	GPU_framebuffer_clear_color_depth(fbl->temp_fb_b, clearcol, 1.0f);
	DRW_draw_pass_subset(
		psl->fx_shader_pass_blend,
		fxd->runtime.fx_sh_c, fxd->runtime.fx_sh_c);

	/* copy pass from b to a for ping-pong frame buffers */
	e_data->input_depth_tx = e_data->temp_depth_tx_b;
	e_data->input_color_tx = e_data->temp_color_tx_b;

	GPU_framebuffer_bind(fbl->temp_fb_a);
	GPU_framebuffer_clear_color_depth(fbl->temp_fb_a, clearcol, 1.0f);
	DRW_draw_pass(psl->mix_pass_noblend);
}

/* helper to draw GLOW passes */
static void draw_gpencil_glow_passes(
        GPENCIL_e_data *e_data,
        GPENCIL_Data *vedata,
        GlowShaderFxData *fxd)
{
	if (fxd->runtime.fx_sh_b == NULL) {
		return;
	}

	GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
	GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl;

	const float clearcol[4] = {0.0f};

	/* prepare glow */
	GPU_framebuffer_bind(fbl->temp_fb_fx);
	GPU_framebuffer_clear_color_depth(fbl->temp_fb_fx, clearcol, 1.0f);
	DRW_draw_pass_subset(
		psl->fx_shader_pass_blend,
		fxd->runtime.fx_sh, fxd->runtime.fx_sh);

	/* blur glow */
	draw_gpencil_do_blur(
	        e_data, vedata, &fxd->runtime,
	        fxd->samples,
	        fxd->blur[0], fxd->blur[0],
	        &fxd->blur[0]);

	/* resolve */
	GPU_framebuffer_bind(fbl->temp_fb_b);
	GPU_framebuffer_clear_color_depth(fbl->temp_fb_b, clearcol, 1.0f);

	/* reuses blur field to keep alpha mode */
	fxd->blur[1] = (fxd->flag & FX_GLOW_USE_ALPHA) ? 1 : 0;

	DRW_draw_pass_subset(
		psl->fx_shader_pass_blend,
		fxd->runtime.fx_sh_c, fxd->runtime.fx_sh_c);

	/* copy pass from b to a for ping-pong frame buffers */
	e_data->input_depth_tx = e_data->temp_depth_tx_b;
	e_data->input_color_tx = e_data->temp_color_tx_b;

	GPU_framebuffer_bind(fbl->temp_fb_a);
	GPU_framebuffer_clear_color_depth(fbl->temp_fb_a, clearcol, 1.0f);
	DRW_draw_pass(psl->mix_pass_noblend);
}

/* apply all object fx effects */
void DRW_gpencil_fx_draw(
        GPENCIL_e_data *e_data,
        GPENCIL_Data *vedata, tGPencilObjectCache *cache)
{
	GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
	GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
	GPENCIL_FramebufferList *fbl = ((GPENCIL_Data *)vedata)->fbl;

	/* loop FX modifiers */
	for (ShaderFxData *fx = cache->shader_fx.first; fx; fx = fx->next) {
		if (effect_is_active(cache->gpd, fx, stl->storage->is_render)) {
			switch (fx->type) {

				case eShaderFxType_Blur:
				{
					BlurShaderFxData *fxd = (BlurShaderFxData *)fx;
					draw_gpencil_blur_passes(e_data, vedata, fxd);
					break;
				}
				case eShaderFxType_Colorize:
				{
					ColorizeShaderFxData *fxd = (ColorizeShaderFxData *)fx;
					gpencil_draw_fx_pass(e_data, psl, fbl, fxd->runtime.fx_sh, false);
					break;
				}
				case eShaderFxType_Flip:
				{
					FlipShaderFxData *fxd = (FlipShaderFxData *)fx;
					gpencil_draw_fx_pass(e_data, psl, fbl, fxd->runtime.fx_sh, false);
					break;
				}
				case eShaderFxType_Light:
				{
					LightShaderFxData *fxd = (LightShaderFxData *)fx;
					gpencil_draw_fx_pass(e_data, psl, fbl, fxd->runtime.fx_sh, false);
					break;
				}
				case eShaderFxType_Pixel:
				{
					PixelShaderFxData *fxd = (PixelShaderFxData *)fx;
					gpencil_draw_fx_pass(e_data, psl, fbl, fxd->runtime.fx_sh, false);
					break;
				}
				case eShaderFxType_Rim:
				{
					RimShaderFxData *fxd = (RimShaderFxData *)fx;
					draw_gpencil_rim_passes(e_data, vedata, fxd);
					break;
				}
				case eShaderFxType_Shadow:
				{
					ShadowShaderFxData *fxd = (ShadowShaderFxData *)fx;
					draw_gpencil_shadow_passes(e_data, vedata, fxd);
					break;
				}
				case eShaderFxType_Glow:
				{
					GlowShaderFxData *fxd = (GlowShaderFxData *)fx;
					draw_gpencil_glow_passes(e_data, vedata, fxd);
					break;
				}
				case eShaderFxType_Swirl:
				{
					SwirlShaderFxData *fxd = (SwirlShaderFxData *)fx;
					gpencil_draw_fx_pass(e_data, psl, fbl, fxd->runtime.fx_sh, false);
					break;
				}
				case eShaderFxType_Wave:
				{
					WaveShaderFxData *fxd = (WaveShaderFxData *)fx;
					gpencil_draw_fx_pass(e_data, psl, fbl, fxd->runtime.fx_sh, false);
					break;
				}
				default:
					break;
			}
		}
	}
}
